import type {
  AllDragTypes,
  DropTargetRecord,
  Input
} from '@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types'
/**
 * @用途 拖放处理
 * @author 黄敏
 * @创建时间 2025-08-19 11:05
 **/
import type { Ref } from 'vue'
import type { DragData, DragType } from '@/composables/dragAndDrop/dragDropUtil'
import {
  attachClosestEdge,
  type Edge,
  extractClosestEdge
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge'
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
import { dropTargetForExternal } from '@atlaskit/pragmatic-drag-and-drop/external/adapter'
import isEmpty from 'lodash/isEmpty'

/**
 * 边栏标签页拖拽状态类型
 */
export type TabDropState = { type: 'idle' } | { type: 'drag-over'; closestEdge: Edge | null }

interface UseDropParams<T extends DragType[]> {
  // 标签页拖拽元素
  el: Ref<HTMLElement | undefined>

  /** 接受的拖拽类型 */
  acceptedTypes: T
  // 放置回调
  onDrop: (sourceData: Pick<DragData, T[number]>, edge: Edge | null, isExternal?: boolean) => void
  // 判断是否允许放置
  canDrop?: (source: Pick<DragData, T[number]>, input: Input, isExternal?: boolean) => boolean
  // 允许的边缘
  allowedEdges?: Edge[]
  /** 禁止拖拽 */
  noDrag?: boolean
}

export function useDropHandle<T extends DragType[]>({
  el,
  acceptedTypes,
  canDrop,
  onDrop,
  allowedEdges,
  noDrag
}: UseDropParams<T>) {
  type Data = Pick<DragData, T[number]>

  /** 默认空闲状态 */
  const IDLE_STATE: TabDropState = { type: 'idle' }

  /** 清理函数，用于取消拖拽相关的事件监听器 */
  let cleanupInternalDropListeners = () => {}
  let cleanupExternalDropListeners = () => {}

  /**
   * 验证是否为有效的边栏标签页拖拽数据
   */
  function getDragData(data: Record<string, unknown>) {
    for (const key of acceptedTypes) {
      if (data[key]) {
        return data as Data
      }
    }
  }

  /** 当前标签页的拖拽状态 */
  const dropState = shallowRef<TabDropState>(IDLE_STATE)

  /**
   * 获取放置目标的数据
   * 附加最近边缘信息，用于确定插入位置
   */
  function getData({ input, element }: { input: Input; element: Element }) {
    return attachClosestEdge(
      {},
      {
        element,
        input,
        allowedEdges: allowedEdges ?? ['left', 'right']
      }
    )
  }

  /**
   * 拖拽进入目标区域时的回调
   * 更新状态并显示拖拽指示器
   */
  function onDragEnter(data: { self: DropTargetRecord }) {
    const closestEdge = extractClosestEdge(data.self.data)
    dropState.value = { type: 'drag-over', closestEdge }
  }

  /**
   * 拖拽在目标区域内移动时的回调
   * 实时更新最近边缘信息
   */
  function onDrag(params: { self: DropTargetRecord }) {
    const closestEdge = extractClosestEdge(params.self.data)

    // 只有当边缘位置发生变化时才更新状态，避免不必要的重渲染
    if (dropState.value.type !== 'drag-over' || dropState.value.closestEdge !== closestEdge) {
      dropState.value = { type: 'drag-over', closestEdge }
    }
  }

  /**
   * 设置为粘性目标
   * 确保拖拽过程中目标保持活跃状态
   */
  function getIsSticky() {
    return true
  }

  /**
   * 拖拽离开目标区域时的回调
   * 重置状态为空闲
   */
  function onDragLeave() {
    dropState.value = IDLE_STATE
  }

  function getExternalData(source: AllDragTypes['payload']) {
    const externalTab: Record<string, unknown> = {}

    if ('types' in source) {
      source.types.forEach((type: string) => {
        if (type) {
          externalTab[type] = JSON.parse(source.getStringData(type) || 'null')
        }
      })
    }

    return externalTab as Data
  }

  onMounted(() => {
    if (!el.value || noDrag) return // 元素引用无效或禁止拖拽，退出
    cleanupInternalDropListeners = dropTargetForElements({
      element: el.value,
      canDrop: ({ source, input }) => {
        // 不允许放置到自己身上
        const data = getDragData(source.data)
        if (source.element === el.value || !data) return false

        return canDrop ? canDrop(data, input) : true
      },
      getData,
      onDragEnter,
      onDrag,
      getIsSticky,
      onDragLeave,
      /**
       * 拖拽放置完成时的回调
       * 执行标签页移动操作并重置状态
       */
      onDrop({ source, location }) {
        dropState.value = IDLE_STATE
        const target = location.current.dropTargets[0]
        if (!target) {
          return
        }
        const data = getDragData(source.data)
        if (!data) {
          return
        }

        onDrop(data, extractClosestEdge(target.data))
      }
    })

    /**
     * 创建外部拖拽目标配置（跨窗口拖拽）
     * @returns 外部拖拽目标配置对象
     */
    cleanupExternalDropListeners = dropTargetForExternal({
      element: el.value,

      /**
       * 检查是否可以接受外部拖拽放置
       * 只允许来自其他窗口的标签页拖拽
       */
      canDrop({ source, input }) {
        dropState.value = IDLE_STATE
        const types = source.types as T
        // 检查拖拽边界限制
        const boolean = types.some(v => acceptedTypes.includes(v))
        if (!boolean) return false

        return canDrop ? canDrop(getExternalData(source), input, true) : true
      },

      getData,
      onDragEnter,
      onDrag,
      onDragLeave,
      getIsSticky,

      /**
       * 外部拖拽放置完成时的回调
       * 处理跨窗口标签页移动操作并重置状态
       */
      onDrop({ source, location }) {
        dropState.value = IDLE_STATE

        const externalTab = getExternalData(source)

        const target = location.current.dropTargets[0]

        if (!target || isEmpty(externalTab)) return

        onDrop(externalTab as Data, extractClosestEdge(target.data), true)
      }
    })
  })

  onUnmounted(() => {
    cleanupInternalDropListeners()
    cleanupExternalDropListeners()
  })

  return {
    dropState
  }
}
